#include "widget.h"

#define TIMEOUT 100

float transparentVertices[] = {
    // positions          // texture Coords (note we set these higher than 1 (together with GL_REPEAT as texture wrapping mode). this will cause the floor texture to repeat)
    25.0f, 0.0f,  25.0f,  1.0f, 0.0f,
    -25.0f, 0.0f, -25.0f,  0.0f, 1.0f,
    -25.0f, 0.0f,  25.0f,  0.0f, 0.0f,


    25.0f, 0.0f,  25.0f,  1.0f, 0.0f,
    25.0f, 0.0f, -25.0f,  1.0f, 1.0f,
    -25.0f, 0.0f, -25.0f,  0.0f, 1.0f,

};


float planeVertices[] = {
    // positions         // texture Coords (swapped y coordinates because texture is flipped upside down)
    0.0f, -2.5f,  0.0f,  0.0f,  0.0f,
    0.0f, 2.5f,  0.0f,  0.0f,  1.0f,
    5.0f, 2.5f,  0.0f,  1.0f,  1.0f,

    0.0f, -2.5f,  0.0f,  0.0f,  0.0f,
    5.0f, 2.5f,  0.0f,  1.0f,  1.0f,
    5.0f, -2.5f,  0.0f,  1.0f,  0.0f
};


MyGLWidget::MyGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
    m_camera.Position = QVector3D(0.0,5.0,25.0);

    m_timer = new QTimer(this);
    connect(m_timer,&QTimer::timeout,this,[&]
    {
        update();
    });
    m_timer->start(TIMEOUT);
    m_time.start();
}

MyGLWidget::~MyGLWidget()
{
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
    glDeleteFramebuffers(1, &fbo);
}

void MyGLWidget::initializeGL()
{
    initializeOpenGLFunctions(); // 初始化 OpenGL 函数

    //创建、绑定VAO
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    //创建、绑定VBO + 填充数据
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);


    vector<float> _vertices;
    vector<unsigned int> _indices;
    for(int i=0;i<6;i++)
    {
        _vertices.push_back(planeVertices[i*5+0]);
        _vertices.push_back(planeVertices[i*5+1]);
        _vertices.push_back(planeVertices[i*5+2]);
        _vertices.push_back(planeVertices[i*5+3]);
        _vertices.push_back(planeVertices[i*5+4]);
        _indices.push_back(i);
    }

    glBufferData(GL_ARRAY_BUFFER, _vertices.size()* sizeof(float), &_vertices[0], GL_STATIC_DRAW);

    glGenBuffers(1,&EBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,
                               _indices.size() * sizeof(unsigned int),&_indices[0], GL_STATIC_DRAW);

    //设置顶点属性指针
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    //纹理坐标
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    //解绑缓冲区和 VAO
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    shader.install(":/shader/shader.vert", ":/shader/shader.frag");

    //创建、绑定纹理
    texture1 = loadTexture(":/img/wall.jpg", false);
    texture2 = loadTexture(":/img/awesomeface.png");

    // 绑定着色器程序
    shader.bind();
    // 设置纹理采样器
    shader.setUniformValue("texture1", 0); // 设置采样器 texture1 到纹理单元 0
    shader.setUniformValue("texture2", 1); // 设置采样器 texture2 到纹理单元 1
    shader.setUniformValue("ratio",ratio);


    /***************fbo****************/
    //创建、绑定VAO
    glGenVertexArrays(1, &f_vao);
    glBindVertexArray(f_vao);

    //创建、绑定VBO + 填充数据
    glGenBuffers(1, &f_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, f_vbo);


    vector<float> f_vertices;
    vector<unsigned int> f_indices;
    for(int i=0;i<6;i++)
    {
        f_vertices.push_back(transparentVertices[i*5+0]);
        f_vertices.push_back(transparentVertices[i*5+1]);
        f_vertices.push_back(transparentVertices[i*5+2]);
        f_vertices.push_back(transparentVertices[i*5+3]);
        f_vertices.push_back(transparentVertices[i*5+4]);
        f_indices.push_back(i);
    }

    glBufferData(GL_ARRAY_BUFFER, f_vertices.size()* sizeof(float), &f_vertices[0], GL_STATIC_DRAW);

    glGenBuffers(1,&f_ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, f_ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,
                               _indices.size() * sizeof(unsigned int),&_indices[0], GL_STATIC_DRAW);

    //设置顶点属性指针
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    //纹理坐标
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    //解绑缓冲区和 VAO
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    //建一个帧缓冲对象fbo
    glGenFramebuffers(1, &fbo);

    //绑定帧缓冲
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);

    //为帧缓冲创建一个纹理
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width(), height(), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    //附加到帧缓冲上
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);

    //创建一个渲染缓冲对象rbo
    unsigned int rbo;
    glGenRenderbuffers(1, &rbo);
    glBindRenderbuffer(GL_RENDERBUFFER, rbo);

    //分配存储空间
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width(), height());

    //附加这个渲染缓冲对象
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
    //解绑这个渲染缓冲
    glBindRenderbuffer(GL_RENDERBUFFER, 0);


    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

void MyGLWidget::paintGL()
{
    QMatrix4x4 model, view, projection;

    projection.setToIdentity();
    projection.perspective(m_camera.Zoom, (float)width() / (float)height(), 0.1f, 100.0f);

    view = m_camera.GetViewMatrix();

    shader.bind();
    shader.setUniformValue("view", view);
    shader.setUniformValue("projection", projection);

    model.setToIdentity();
    shader.setUniformValue("model", model);
#if 1
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glEnable(GL_DEPTH_TEST);//启用深度测试
    glDepthFunc(GL_LESS);
    //glDepthMask(GL_FALSE);//禁用深度缓冲的写入
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色缓冲区

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture1);
    //glActiveTexture(GL_TEXTURE1); // 激活纹理单元 1
    //glBindTexture(GL_TEXTURE_2D, texture2); // 绑定第二个纹理

    glBindVertexArray(f_vao);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
#endif

#if 01
    /**********************************/
    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());// 返回默认

    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glDisable(GL_DEPTH_TEST);
    //glDepthMask(GL_FALSE);//禁用深度缓冲的写入
    glClear(GL_COLOR_BUFFER_BIT); // 清除颜色缓冲区

    model.setToIdentity();
    shader.setUniformValue("model", model);
    glBindVertexArray(f_vao);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture1);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);


    vector<QVector3D> windows;
    windows.push_back(QVector3D( 0.0f, 2.5f, 0.7f));
    map<float, QVector3D> sorted;
    foreach(auto item,windows) {
        float distance =
            m_camera.Position.distanceToPoint(item);
        sorted[distance] = item;
    }

    //shader.bind();
    glBindVertexArray(VAO);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);//texture1

    for(map<float,QVector3D>::reverse_iterator riter=sorted.rbegin();
         riter!=sorted.rend();riter++)
    {
        model.setToIdentity();
        model.translate(riter->second);
        shader.setUniformValue("model", model);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    }
    //
#endif
    glBindVertexArray(0);
}

void MyGLWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h); // 设置视口大小
}

unsigned int MyGLWidget::loadTexture(const char *fileName, bool alpha)
{
    unsigned int texture;
    glGenTextures(1, &texture); // 生成纹理 ID
    glBindTexture(GL_TEXTURE_2D, texture); // 绑定纹理

    // 设置纹理参数
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#if 0
    // 加载图片并转换为 OpenGL 格式
    QImage image;
    if (!image.load(fileName))
    {
        qWarning() << "Failed to load texture image";
        return 0;
    }
    image = QGLWidget::convertToGLFormat(image); // 转换为 OpenGL 格式
    unsigned char *data = image.bits();
    // 生成纹理
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D); // 生成多级渐远纹理
#else

    QFile file(fileName);
    file.copy(file.fileName(), QFileInfo(file).fileName());

    int width, height, nrChannels;
    stbi_set_flip_vertically_on_load(true); // tell stb_image.h to flip loaded texture's on the y-axis.
    unsigned char *data = stbi_load(QFileInfo(file).fileName().toStdString().c_str(), &width, &height, &nrChannels, 0);
    if (data)//awesomeface  container
    {
        if(alpha)
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        else
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        std::cout << "Failed to load texture1" << std::endl;
    }
    stbi_image_free(data);
#endif
    return texture;
}

void MyGLWidget::keyPressEvent(QKeyEvent *event)
{
#if 0
    float deltatime=TIMEOUT / 1000.0;
    switch(event->key())
    {
    case Qt::Key_Up:ratio += 0.1;break;
    case Qt::Key_Down:ratio -= 0.1;break;
    case Qt::Key_W: m_camera.ProcessKeyboard(FORWARD,deltatime);break;
    case Qt::Key_S: m_camera.ProcessKeyboard(BACKWARD,deltatime);break;
    case Qt::Key_D: m_camera.ProcessKeyboard(RIGHT,deltatime);break;
    case Qt::Key_A: m_camera.ProcessKeyboard(LEFT,deltatime);break;
    }

    if(ratio > 1) ratio = 1;
    if(ratio < 0) ratio = 0;

    makeCurrent();
    shader.bind();
    shader.setUniformValue("ratio",ratio);
    update();
    doneCurrent();
#endif
}

void MyGLWidget::mouseMoveEvent(QMouseEvent *event)
{
    #if 0
    qDebug() << "mouseMoveEvent";
    static QPoint lastPos(width()/2,height()/2);
    auto currentPos=event->pos();
    deltaPos=currentPos-lastPos;
    lastPos=currentPos;

    m_camera.ProcessMouseMovement(deltaPos.x(),-deltaPos.y());
    update();
    #endif
}

void MyGLWidget::wheelEvent(QWheelEvent *event)
{
#if 0
    qDebug() << "wheelEvent";
    m_camera.ProcessMouseScroll(event->angleDelta().y()/120);
    update();
#endif
}
